function snapValue(value,snap)
{
  var roundedSnap = (value/snap + (value > 0 ? .5 : -.5)) | 0;
  return roundedSnap * snap;
}

/**
 * genera un contenedor con un fondo a cuadros dibujado.
 * @param  {Number} numX cantidad de lineas en eje X
 * @param  {Number} numY cantidad de lineas en eje Y
 * @param  {Number} w    anchura de contenedor sobre el cual dibujar.
 * @param  {Number} h    altura del contenedor.
 * @return {createjs.Container}      Nuevo contenedor con cuadricula dibujada.
 */
 function createBgGrid(numX, numY, w, h) {
    var Container = createjs.Container;
    var Graphics = createjs.Graphics;
    var Shape = createjs.Shape;

    var grid = new Container();
    grid.snapToPixel = true;
        // calculating the distance between
        // the grid-lines
        var gw = w / numX;
        var gh = h / numY;
        // drawing the vertical line
        var verticalLine = new Graphics();
        verticalLine.beginFill(Graphics.getRGB(101, 60, 176));
        verticalLine.drawRect(0, 0, gw * 0.03, gh * (numY + 2));
        var vs;
        // placing the vertical lines:
        // we're placing 1 more than requested
        // to have seamless scrolling later
        for (var c = -1; c < numX + 1; c++) {
            vs = new Shape(verticalLine);
            vs.snapToPixel = true;
            vs.x = c * gw;
            vs.y = -gh;
            grid.addChild(vs);
        }
        // drawing a horizontal line
        var horizontalLine = new Graphics();
        horizontalLine.beginFill(Graphics.getRGB(101, 60, 176));
        horizontalLine.drawRect(0, 0, gw * (numX + 1), gh * 0.03);
        var hs;
        // placing the horizontal lines:
        // we're placing 1 more than requested
        // to have seamless scrolling later
        for (c = -1; c < numY + 1; c++) {
            hs = new Shape(horizontalLine);
            hs.snapToPixel = true;
            hs.x = 0;
            hs.y = c * gh;
            grid.addChild(hs);
        }

        // return the grid-object
        return grid;
    } //createBgGrid


/**
 * Verifica la interseccion entre dos rectangulos
 * @param  {Object} rect1 {x,y,w,h}
 * @param  {Object} rect2 {x,y,w,h}
 * @return {Object}       cero en caso de no inteseccion
 */
 function checkRectCollision(rect1, rect2) {
    rect1.width = rect1.width || rect1.w;
    rect2.width = rect2.width || rect2.w;

    if (rect1.x < rect2.x + rect2.width &&
        rect1.x + rect1.width > rect2.x &&
        rect1.y < rect2.y + rect2.height &&
        rect1.height + rect1.y > rect2.y) {
        return 1;
}

return 0;
    } //checkRectCollision


/**
 * Escribe texto sobre un canvas
 * @param  {String} text      texto a escribir
 * @param  {Number} x         posX
 * @param  {Number} y         posY
 * @param  {String} fillStyle color de texto
 * @param  {String} font      fuente
 * @param  {Canvas} canvas    canvas sobre el cual escribir
 */
 function writeOnCanvas(text, x, y, fillStyle, font, canvas) {
    var canvas = canvas || $("canvas")[0];
    var context = canvas.getContext("2d");
    context.fillStyle = fillStyle || "blue";
    context.font = font || "bold 16px Arial";
    context.fillText(text, x, y);
}//writeOnCanvas

/**
 * ajusta el scrolling del escenario de acuerdo a la posicion del heroe.
 * @param  {Object} hero - Personaje central.
 * @param  {Container} world - Container a partir del cual se ajusta posicion de la pantalla.
 * @param  {Number} w - anchura de canvas
 * @param  {Number} h - altura de canvas.
 * @param  {Number} wd - factor de anchura de ajuste.
 * @param  {Number} hd - factor de altura de ajuste.
 */
 function adjustScrolling(hero, world, w, h, wd, hd) {
    if (hero.x > w * wd) {
        world.x = -hero.x + w * wd;
    }
    if (hero.y > h * hd) {
        world.y = -hero.y + h * hd;
    }
}//adjustScrolling

function adjustScrolling_bak(hero, world, w, h, wd, hd) {
    if (hero.x > w * wd) {
        world.x = -hero.x + w * wd;
    }
    if (hero.y > h * hd) {
        world.y = -hero.y + h * hd;
    } else if (hero.y < h * hd) {
        world.y = -hero.y + h * hd;
    }
}

/**
 * ajusta el tamano del canvas.
 * @param {Number} w - anchura
 * @param {Number} h - altura
 * @param {Canvas} canvas - canvas a ajustar tamano. En caso de no indicarlo, se tomara el canvas principal del .htm
 */
 function setCanvasSize(w, h, canvas) {
    var canvas = canvas || $("canvas")[0];
    if (w) {
        canvas.width = w;
    }
    if (h) {
        canvas.height = h;
    }
}//setCanvasSize


function SetFullScreen(scale, canvas) {
    var canvas = canvas || $("canvas")[0];

    var scale = scale || 1;

    setCanvasSize(getWidth() * scale, getHeight() * scale);
};//SetFullScreen

/**
 * @deprecated calcula los margenes de forma desfazada. Usar calculateRectIntersection
 *
 * Calcula la interseccion entre dos rectangulos: uno en movimiento y otro estatico.
 * @param  {Rectangle} rect1 rectangulo en movimiento.
 * @param  {Rectangle} rect2 rectangulo estatico.
 * @param  {Number} x     tasa de movimiento x de rect1.
 * @param  {Number} y     tasa de movimiento y de rect1.
 * @return {Object}       anchura y altura de interseccion {width,height}.
 */
 function calculateIntersection(rect1, rect2, x, y) {
    // prevent x|y from being null||undefined
    x = x || 0;
    y = y || 0;
    /*x=Math.abs(x);
    y=Math.abs(y);*/


    // first we have to calculate the
    // center of each rectangle and half of
    // width and height
    var dx, dy, r1 = {},
    r2 = {};
    r1.hw = (rect1.width / 2);
    r1.hh = (rect1.height / 2);
    r2.hw = (rect2.width / 2);
    r2.hh = (rect2.height / 2);


    r1.cx = rect1.x + x + r1.hw;
    r1.cy = rect1.y + y + r1.hh;

    /*r1.cx = rect1.x + x +rect1.width;
    r1.cy = rect1.y + y +rect1.height;*/

    r2.cx = rect2.x + r2.hw;
    r2.cy = rect2.y + r2.hh;

    dx = Math.abs(r1.cx - r2.cx) - (r1.hw + r2.hw);
    dy = Math.abs(r1.cy - r2.cy) - (r1.hh + r2.hh);

    if (dx < 0 && dy < 0) {
        return {
            width: -dx,
            height: -dy
        };
    } else {
        return null;
    }
}


/**
 * Calcula interseccion entre dos cuerpos utilizando la logica de google.
 * @param  {Object} go1 GameObject||BitmapAnimation
 * @param  {Object} go2 GameObject||BitmapAnimation
 * @return {goog.math.Rect} Rectangulo que representa la interseccion entre los cuerpoos.
 */
 function calculateRectIntersection(go1, go2) {
    var hb = getBounds(go1);
    hb.x -= hb.width / 2;
    hb.y -= hb.height / 2;
    var rect1 = new goog.math.Rect(hb.x, hb.y, hb.width, hb.height);

    var pb = getBounds(go2);
    pb.x -= pb.width / 2;
    pb.y -= pb.height / 2;
    var rect2 = new goog.math.Rect(pb.x, pb.y, pb.width, pb.height);
    var inter = goog.math.Rect.intersection(rect1, rect2);

    return inter;
}



/**
 * @deprecated calcula colision entre un cuerpo y varios. Usar calculateRectCollision.
 *
 * Calcula la una colision entre un objeto y otros.
 * @param  {Object} obj          Container||Bitmap||BitmapAnimation.
 * @param  {String} direction    Direccion de calculo: 'x'||'y'.
 * @param  {Array} collideables arreglo de objetos a verificar colision.
 * @param  {Object} moveBy       tasa de movimiento de obj en terminos de x e y. Este objeto se modificara con el movimiento que obj debera sufrir.
 * @return {Object}              colision sufrida con diferencial de interseccion entre objetos en terminos de x e y {width,height}
 */
 function calculateCollision(obj, direction, collideables, moveBy) {
    for (var i = 0; i < collideables.length; i++) {
        if (collideables[i] instanceof GameObject) {
            collideables[i] = collideables[i].animation;
        }
    }
    if (obj instanceof GameObject) {
        obj = obj.animation;
    }

    moveBy = moveBy || {
        x: 0,
        y: 0
    };
    if (direction != 'x' && direction != 'y') {
        direction = 'x';
    }
    var measure = direction == 'x' ? 'width' : 'height',
    oppositeDirection = direction == 'x' ? 'y' : 'x',
    oppositeMeasure = direction == 'x' ? 'height' : 'width',

    bounds = getBounds(obj),
    cbounds,
    collision = null,
    cc = 0;

        // for each collideable object we will calculate the
        // bounding-rectangle and then check for an intersection
        // of the hero's future position's bounding-rectangle
        while (!collision && cc < collideables.length) {
            cbounds = getBounds(collideables[cc]);
            if (collideables[cc].isVisible) {
                collision = calculateIntersection(bounds, cbounds, moveBy.x, moveBy.y);
            }

            if (!collision && collideables[cc].isVisible) {
                // if there was NO collision detected, but somehow
                // the hero got onto the "other side" of an object (high velocity e.g.),
                // then we will detect this here, and adjust the velocity according to
                // it to prevent the Hero from "ghosting" through objects
                // try messing with the 'this.velocity = {x:0,y:125};'
                // -> it should still collide even with very high values
                var wentThroughForwards = (bounds[direction] < cbounds[direction] && bounds[direction] + moveBy[direction] > cbounds[direction]),
                wentThroughBackwards = (bounds[direction] > cbounds[direction] && bounds[direction] + moveBy[direction] < cbounds[direction]),
                withinOppositeBounds = !(bounds[oppositeDirection] + bounds[oppositeMeasure] < cbounds[oppositeDirection]) && !(bounds[oppositeDirection] > cbounds[oppositeDirection] + cbounds[oppositeMeasure]);

                if ((wentThroughForwards || wentThroughBackwards) && withinOppositeBounds) {
                    moveBy[direction] = cbounds[direction] - bounds[direction];
                } else {
                    cc++;
                }
            }

            if (collision) {
                collision.cbounds = cbounds;
            }
        }

        if (collision) {
            var sign = Math.abs(moveBy[direction]) / moveBy[direction];
            moveBy[direction] -= collision[measure] * sign;
        }

        return collision;
    } //calculateCollision


    function RectCollision(rect, body) {
        this.width = rect.width;
        this.height = rect.height;
        this.collisioned = this.body = body;
    }

/**
 * calcula colision entre cuerpos de tipo GameObject como rectangulos goog.math.Rect
 * @param  {Object} obj          GameObject||BitmapAnimation.
 * @param  {Array<GameObject>} cuerpos colisionables
 * @return {Array}              arreglo de colisiones.
 */
 function calculateRectCollision(obj, collideables) {
    var collisions = null;

    for (var i = 0; i < collideables.length; i++) {
        var collideable = collideables[i];
        var inter = calculateRectIntersection(obj, collideable);
        if (inter) {
            collisions = collisions || [];
            collisions.push(new RectCollision(inter, collideable));
        }
    }

    return collisions;
    } //calculateGoogMathRectCollision

/*
 * Calculated the boundaries of an object.
 *
 * CAUTION: <rotation> OR <skew> attributes are NOT used for this calculation!
 *
 * @method getBounds
 * @param {DisplayObject} the object to calculate the bounds from. Container||Bitmap||BitmapAnimation||GameObject.
 * @return {Rectangle} The rectangle describing the bounds of the object
 */
 function getBounds(obj) {
    if (obj instanceof GameObject) {
        obj = obj.animation;
            //return obj.getRectBounds();
        }

        var bounds = {
            x: Infinity,
            y: Infinity,
            width: 0,
            height: 0
        };

        if (obj instanceof createjs.Container) {
            var children = object.children,
            l = children.length,
            cbounds, c;
            for (c = 0; c < l; c++) {
                cbounds = getBounds(children[c]);
                if (cbounds.x < bounds.x) bounds.x = cbounds.x;
                if (cbounds.y < bounds.y) bounds.y = cbounds.y;
                if (cbounds.width > bounds.width) bounds.width = cbounds.width;
                if (cbounds.height > bounds.height) bounds.height = cbounds.height;
            }
        } else {
            var gp, imgr;
            if (obj instanceof createjs.Bitmap) {
                gp = obj.localToGlobal(0, 0);
                imgr = {
                    width: obj.image.width,
                    height: obj.image.height
                };
            } else if (obj instanceof createjs.BitmapAnimation) {
                gp = obj.localToGlobal(0, 0);
                imgr = obj.spriteSheet._frames[obj.currentFrame];
            } else {
                return bounds;
            }


            bounds.width = imgr.width || imgr.rect.width;
            bounds.width *= Math.abs(obj.scaleX);
            if (obj.scaleX >= 0) {
                bounds.x = gp.x;
            } else {
                bounds.x = gp.x - bounds.width;
            }

            bounds.height = imgr.rect.height * Math.abs(obj.scaleY);
            if (obj.scaleX >= 0) {
                bounds.y = gp.y;
            } else {
                bounds.y = gp.y - bounds.height;
            }

        }

        return bounds;
    } //getBounds

    function getWidth() {
        if (typeof(window.innerWidth) == 'number') {
            return window.innerWidth;
        } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
            return document.documentElement.clientWidth;
        } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
            return document.body.clientWidth;
        }
    }

    function getHeight() {
        if (typeof(window.innerWidth) == 'number') {
            return window.innerHeight;
        } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
            return document.documentElement.clientHeight;
        } else if (document.body && (document.body.clientHeight || document.body.clientHeight)) {
            return document.body.clientHeight;
        }
    }



    function GetLeft(elm) {
        var left = elm.offsetLeft;
        while (elm = elm.offsetParent) {
            left += elm.offsetLeft;
        }

        left -= window.pageXOffset;
        return left;
    };

    function GetTop(elm) {
        var top = elm.offsetTop;
        while (elm = elm.offsetParent) {
            top += elm.offsetTop;
        }

        top -= window.pageYOffset;
        return top;
    };

    Array.prototype.Remove = function(arg, all) {
        for (var i = 0; i < this.length; i++) {
            if (this[i] == arg) {
                this.splice(i, 1);

                if (!all) {
                    break;
                } else {
                    i--;
                }
            }
        }
    };

    Array.prototype.RemoveAt = function(position) {
        this.splice(position, 1);
    };

    Array.prototype.Clear = function() {
        this.length = 0;
    };

    Array.prototype.InsertAt = function(arg, position) {
        this.splice(position, 0, arg);
    };

    Array.prototype.Contains = function(arg) {
        for (var i = 0; i < this.length; i++) {
            if (this[i] == arg) {
                return true;
            }
        }

        return false;
    };

    Array.prototype.Occurs = function(arg) {
        var counter = 0;

        for (var i = 0; i < this.length; i++) {
            if (this[i] == arg) {
                counter++;
            }
        }

        return counter;
    };

    Direction = {
        left: 2,
        up: 3,
        right: 5,
        down: 7,

        upLeft: 6,
        downRight: 35,
        downLeft: 14,
        upRight: 15
    };

    Input = function() {
        this.a = false;
        this.b = false;
        this.c = false;
        this.d = false;
        this.e = false;
        this.f = false;
        this.g = false;
        this.h = false;
        this.i = false;
        this.j = false;
        this.k = false;
        this.l = false;
        this.m = false;
        this.n = false;
        this.o = false;
        this.p = false;
        this.q = false;
        this.r = false;
        this.s = false;
        this.t = false;
        this.u = false;
        this.v = false;
        this.w = false;
        this.x = false;
        this.y = false;
        this.z = false;

        this.up = false;
        this.down = false;
        this.left = false;
        this.right = false;
        this.enter = false;
        this.space = false;

        this.mouseIsDown = false;
        this.mousePosition = new Vector2(0);

        /*offset es top left corner del canvas*/
        this.offset = new Vector2(0);
        this.clamp = new Vector2(0);
    };
//Input

var input = new Input();

document.documentElement.onmousemove = function(e) {
    e = e || window.event;

    input.mousePosition.x = e.clientX - input.offset.x;
    input.mousePosition.y = e.clientY - input.offset.y;

    /*DEBUG!*/
    var s = input.mousePosition.x + "," + input.mousePosition.y;
    if ($("#debug").get(0) !== undefined)
        $("#debug").val(s);
};

$(document).mousedown(function(e) {
    input.mouseIsDown = true;
});

$(document).mouseup(function(e) {
    input.mouseIsDown = false;
});

$(document).keydown(function(e) {
    e.preventDefault();

    var keycode;
    if (window.event) {
        keycode = window.event.keyCode;
    } else {
        keycode = e.which;
    }


    switch (keycode) {
        /*input de teclas especiales*/
        case 32:
        input.space = true;
        break;
        case 13:
        input.enter = true;
        break;
        case 37:
        input.left = true;
        break;
        case 38:
        input.up = true;
        break;
        case 39:
        input.right = true;
        break;
        case 40:
        input.down = true;
        break;

        /*input de letras*/
        case 65:
        input.a = true;
        break;
        case 66:
        input.b = true;
        break;
        case 67:
        input.c = true;
        break;
        case 68:
        input.d = true;
        break;
        case 69:
        input.e = true;
        break;
        case 70:
        input.f = true;
        break;
        case 71:
        input.g = true;
        break;
        case 72:
        input.h = true;
        break;
        case 73:
        input.i = true;
        break;
        case 74:
        input.j = true;
        break;
        case 75:
        input.k = true;
        break;
        case 76:
        input.l = true;
        break;
        case 77:
        input.m = true;
        break;
        case 78:
        input.n = true;
        break;
        case 79:
        input.o = true;
        break;
        case 80:
        input.p = true;
        break;
        case 81:
        input.q = true;
        break;
        case 82:
        input.r = true;
        break;
        case 83:
        input.s = true;
        break;
        case 84:
        input.t = true;
        break;
        case 85:
        input.u = true;
        break;
        case 86:
        input.v = true;
        break;
        case 87:
        input.w = true;
        break;
        case 88:
        input.x = true;
        break;
        case 89:
        input.y = true;
        break;
        case 90:
        input.z = true;
        break;

    };
});

$(document).keyup(function(e) {
    var keycode;
    if (window.event) {
        keycode = window.event.keyCode;
    } else {
        keycode = e.which;
    }

    switch (keycode) {
        /*input de teclas especiales*/
        case 32:
        input.space = false;
        break;
        case 13:
        input.enter = false;
        break;
        case 37:
        input.left = false;
        break;
        case 38:
        input.up = false;
        break;
        case 39:
        input.right = false;
        break;
        case 40:
        input.down = false;
        break;

        /*input de letras*/
        case 65:
        input.a = false;
        break;
        case 66:
        input.b = false;
        break;
        case 67:
        input.c = false;
        break;
        case 68:
        input.d = false;
        break;
        case 69:
        input.e = false;
        break;
        case 70:
        input.f = false;
        break;
        case 71:
        input.g = false;
        break;
        case 72:
        input.h = false;
        break;
        case 73:
        input.i = false;
        break;
        case 74:
        input.j = false;
        break;
        case 75:
        input.k = false;
        break;
        case 76:
        input.l = false;
        break;
        case 77:
        input.m = false;
        break;
        case 78:
        input.n = false;
        break;
        case 79:
        input.o = false;
        break;
        case 80:
        input.p = false;
        break;
        case 81:
        input.q = false;
        break;
        case 82:
        input.r = false;
        break;
        case 83:
        input.s = false;
        break;
        case 84:
        input.t = false;
        break;
        case 85:
        input.u = false;
        break;
        case 86:
        input.v = false;
        break;
        case 87:
        input.w = false;
        break;
        case 88:
        input.x = false;
        break;
        case 89:
        input.y = false;
        break;
        case 90:
        input.z = false;
        break;

    };
});
